package broker

import (
	"context"
	"fmt"
	"gitlab.local/backend/proto/dto"
	"manager/auth"
	"manager/bll"
	"manager/client/grpc"
	log "manager/collector/logger"
	cache "manager/lib/infra"
	"manager/lib/trace_log"
	"manager/model"
	"manager/model/entity"
	"manager/store/postgres"
	"manager/store/redis"
	"manager/utils"
	"strings"
	"time"
)

var faultCategoryMap = map[dto.FaultType]string{
	dto.FaultType_FaultBatteryChargingOverheating:    "charge_monitor",
	dto.FaultType_FaultBatteryChargingLowTemperature: "charge_monitor",
	dto.FaultType_FaultChargingOverCurrent:           "charge_monitor",
}

var FaultReport = &faultReport{}

// 故障上报
type faultReport struct {
}

// HandlerFaultCheckReport 处理故障检测上报
func (f *faultReport) HandlerFaultCheckReport(data *dto.FaultCheck) error {
	// 1. 获取车辆信息
	bikeId, err := bll.CacheBike.GetBikeIdBySn(data.Sn)
	if err != nil {
		return err
	}

	// 扫描故障列表，进行删除
	var faultMap = make(map[dto.FaultType]struct{})
	for _, v := range data.FaultList {
		faultMap[v.Type] = struct{}{}
	}

	var faultEunms []int64
	for i := int(dto.FaultType_FaultEngine); i <= int(dto.FaultType_FaultBatteryEOFEOL); i++ {
		if _, ok := faultMap[dto.FaultType(i)]; ok {
			continue
		}

		faultEunms = append(faultEunms, int64(i))
	}

	// 清除故障
	//_ = postgres.Fault.ClearFault(context.Background(), bikeId, faultEunms)

	// 清理提示
	type Ret struct {
		Num   int64 `json:"num"`
		Level int   `json:"level"`
	}

	var ret []*Ret

	postgres.GetDB(context.Background()).Model(&entity.Fault{}).Select("level, count(*) num").Where("ebike_id = ? and fault_enum in ?", bikeId, faultEunms).Group("level").Scan(&ret)

	if len(ret) > 0 {
		levelMap := make(map[int]int64)
		for _, v := range ret {
			levelMap[v.Level] = v.Num
		}

		var clearLevel []string
		for i := 1; i <= 3; i++ {
			if _, ok := levelMap[i]; ok {
				clearLevel = append(clearLevel, fmt.Sprintf("'%v'", i))
			}
		}

		if len(clearLevel) > 0 {
			// 清理已经处理了的故障提示
			postgres.GetDB(context.Background()).Where(fmt.Sprintf("ebike_id = %v and message_type = 1 and extend in (%v)", bikeId, strings.Join(clearLevel, ","))).Delete(&entity.MessageNotify{})
		}
	}

	trace_log.DebugLogger(data.Sn, "---- 清理故障 bikeId:", bikeId, " faultEunms:", utils.ToJsonString(faultEunms), " ret:", utils.ToJsonString(ret))
	return nil
}

// HandlerFaultReport 处理故障上报
func (f *faultReport) HandlerFaultReport(data *dto.Fault) error {
	// 1. 获取车辆信息
	bikeId, err := bll.CacheBike.GetBikeIdBySn(data.Sn)
	if err != nil {
		return err
	}

	// 查询缓存
	var bikeSettings *entity.Setting
	if settings, ok := cache.GCache.Get(fmt.Sprintf("%v", bikeId)); !ok {
		// 查询车辆设置
		bikeSettings, err = postgres.Setting.Find(&model.SettingUpdateRequest{
			EbikeId: &bikeId,
		})

		cache.GCache.Set(fmt.Sprintf("%v", bikeId), bikeSettings, time.Second*30)
	} else {
		bikeSettings = settings.(*entity.Setting)
	}

	// 当前车辆故障未开启上报
	if bikeSettings != nil && bikeSettings.EbikeSet != nil && bikeSettings.EbikeSet.HitchsUp == 0 {
		trace_log.DebugLogger(data.Sn, "---- 当前车辆故障未开启上报 HitchsUp is 0")
		return nil
	}

	// 自定义车辆信息
	isManager := 1
	userBikeInfo, err := postgres.UserEbike.FindByParameter(context.Background(), &model.UserEbikeRequest{
		EbikeID:   &bikeId,
		IsManager: &isManager,
	})

	if err != nil {
		return err
	}

	// 2. 获取车辆信息
	bikeInfo, _ := bll.Ebike.BikeInfo(context.WithValue(context.Background(), auth.AuthUserKey, userBikeInfo.UserID), bikeId)
	if bikeInfo == nil {
		log.Errorf("HandlerFaultReport bikeInfo is nil")
		return nil
	}

	// 获取故障信息
	faultEnum := int64(data.Type)
	faultTypeInfo, err := bll.FaultType.Find(context.Background(), &model.FaultTypeInfoRequest{
		FaultEnum: &faultEnum,
	})

	if err != nil {
		return err
	}

	// 电话号码
	phone := ""
	if grpc.UCmd != nil {
		outInfo, _ := grpc.UCmd.UserInfo(context.Background(), &dto.UserInfoRequest{
			Id: userBikeInfo.UserID,
		})

		if outInfo != nil {
			phone = outInfo.Phone
		}
	}

	// 创建故障记录
	id, err := postgres.Fault.Create(context.Background(), &entity.Fault{
		UserId:        bll.UserEbike.GetManagerByBikeId(bikeInfo.ID),
		EbikeId:       bikeInfo.ID,
		EbikeName:     bikeInfo.Name,
		EbikeTypeId:   bikeInfo.TypeID,
		EbikeTypeName: bikeInfo.TypeName,
		Phone:         phone,
		FaultId:       faultTypeInfo.Id,
		FaultName:     faultTypeInfo.Name,
		FaultDesc:     faultTypeInfo.Desc,
		FaultEnum:     faultTypeInfo.FaultEnum,
		Level:         faultTypeInfo.Level,
		SnCode:        bikeInfo.SNCode,
	})

	if err != nil {
		return err
	}

	// 加分布式锁
	conn := redis.KV.GetRedisPool().Get()
	lock := &redis.TRedLock{}
	bSuccess, _ := lock.Lock(conn, fmt.Sprintf("fault_%v", bikeInfo.ID), 10)
	if !bSuccess {
		log.Errorf("HandlerFaultReport lock fail")
		return nil
	}
	defer lock.UnLock(conn)

	// 用户设置判断
	masterId := bll.UserEbike.GetManagerByBikeId(bikeInfo.ID)
	userSettings, err := postgres.UserSetting.Find(context.Background(), &model.UserSettingInfoRequest{
		UserId: &masterId,
	})
	if err != nil || userSettings == nil {
		return fmt.Errorf(fmt.Sprintf("HandlerFaultReport UserSetting is nil bikeId:%v", bikeInfo.ID))
	}

	// 充电监测相关
	isChargeMonitor := false
	for k, v := range faultCategoryMap {
		if v == "charge_monitor" && int64(k) == faultTypeInfo.FaultEnum {
			if userSettings.ChargeMonitor == 0 {
				return nil
			} else {
				isChargeMonitor = true
			}
		}
	}

	// 不是充电监测
	if !isChargeMonitor {
		// 未开启不上报故障
		if userSettings.FaultAlarm <= 0 {
			return nil
		}
	}

	// 如果存在未读的级别一样的，就不再创建提醒
	flag := 0 // 未读
	info, _ := bll.MessageNotify.Find(context.Background(), &model.MessageNotifyInfoRequest{
		EbikeId: &bikeInfo.ID,
		Flag:    &flag,
		Extend:  fmt.Sprintf("%v", faultTypeInfo.Level),
		TimeStamp: func() *int64 {
			t := time.Now().Unix()
			return &t
		}(),
	})

	if info != nil && info.Id > 0 {
		return nil
	}

	// 创建告警通知
	bll.MessageNotify.Create(context.Background(), &model.MessageNotifyCreateRequest{
		EbikeId:     bikeInfo.ID,
		EbikeName:   bikeInfo.Name,
		MessageType: 1,
		Title:       fmt.Sprintf("%v_%v", bikeInfo.Name, faultTypeInfo.Name),
		Content:     f.getInfoContentByLevel(faultTypeInfo.Level),
		Flag:        0,
		Extend:      fmt.Sprintf("%v", faultTypeInfo.Level),
		FrameCode:   bikeInfo.FrameCode,
		RelationId:  id,
	})
	return nil
}

// HandlerAlarmReport 处理告警上报
func (f *faultReport) getInfoContentByLevel(level int) string {
	switch level {
	case 1:
		return "车辆有一个严重的故障，请及时处理！"
	case 2:
		return "车辆有一个中等的故障，请及时处理！"
	case 3:
		return "车辆有一个轻微的故障，请及时处理！"
	}

	return "车辆发生了一个故障，请及时处理！"
}
